from __future__ import annotations

from datetime import date
from unittest.mock import MagicMock
from unittest.mock import Mock
from unittest.mock import patch

import pytest

from payments import PaymentStatus
from payments import RedirectNeeded

from . import BraintreeProvider

MERCHANT_ID = "test11"
PUBLIC_KEY = "abcd1234"
PRIVATE_KEY = "1234abcd"

PROCESS_DATA = {
    "name": "John Doe",
    "number": "371449635398431",
    "expiration_0": "5",
    "expiration_1": date.today().year + 1,
    "cvv2": "1234",
}


class Payment(Mock):
    id = 1
    variant = "braintree"
    currency = "USD"
    total = 100
    status = PaymentStatus.WAITING
    transaction_id = None
    captured_amount = 0

    def get_process_url(self):
        return "http://example.com"

    def get_failure_url(self):
        return "http://cancel.com"

    def get_success_url(self):
        return "http://success.com"

    def change_status(self, status):
        self.status = status


@pytest.fixture
def payment():
    return Payment()


def test_provider_redirects_to_success_on_payment_success(payment):
    provider = BraintreeProvider(MERCHANT_ID, PUBLIC_KEY, PRIVATE_KEY)
    transaction_id = "12345"
    with (
        patch("braintree.Transaction"),
        patch("braintree.Transaction.sale") as mocked_sale,
    ):
        sale = MagicMock()
        sale.is_success = True
        sale.transaction.id = transaction_id
        mocked_sale.return_value = sale
        with pytest.raises(RedirectNeeded) as exc:
            provider.get_form(payment, data=PROCESS_DATA)
        url = exc.value.args[0]
        assert url == payment.get_success_url()
    assert payment.status == PaymentStatus.CONFIRMED
    assert payment.captured_amount == payment.total
    assert payment.transaction_id == transaction_id


def test_provider_shows_validation_error_message(payment):
    provider = BraintreeProvider(MERCHANT_ID, PUBLIC_KEY, PRIVATE_KEY)
    error_msg = "error message"
    with (
        patch("braintree.Transaction"),
        patch("braintree.Transaction.sale") as mocked_sale,
    ):
        sale = MagicMock()
        sale.is_success = False
        sale.message = error_msg
        mocked_sale.return_value = sale
        form = provider.get_form(payment, data=PROCESS_DATA)
        assert form.errors["__all__"][0] == error_msg
    assert payment.status == PaymentStatus.ERROR
    assert payment.captured_amount == 0
